package be.kuleuven.cs.mop.gui.dialogs;


import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.util.Collection;

import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import be.kuleuven.cs.mop.app.controllers.Controllers;
import be.kuleuven.cs.mop.app.controllers.UserController;
import be.kuleuven.cs.mop.domain.exceptions.InvalidCurrentUserException;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.TaskType;
import be.kuleuven.cs.mop.gui.components.ActionButton;
import be.kuleuven.cs.mop.gui.components.ComboSelector;
import be.kuleuven.cs.mop.gui.components.Component;
import be.kuleuven.cs.mop.gui.components.InfoLabel;
import be.kuleuven.cs.mop.gui.components.SelectionList;
import be.kuleuven.cs.mop.gui.components.TextInput;
import be.kuleuven.cs.mop.gui.components.Wrapper;
import be.kuleuven.cs.mop.gui.util.SwingUtils;
import be.kuleuven.cs.mop.util.Utils;



/**
 * {@link ActionDialog} for the "Focus Work" use case
 */
public class FocusWorkDialog extends DynamicActionDialog<String> {
	private static final long serialVersionUID = 1L;
	
	private static final String[] TYPES = {
		"Deadline-based focus",
		"Duration-based focus",
		"Task-type focus"
	};
	
	private Wrapper<Parameters>		input	=
		new Wrapper<Parameters>(new Dimension(500, 300));
	private ComboSelector<String>	type	= new ComboSelector<String>(TYPES);
	
	
	public FocusWorkDialog(final Frame parent, final Controllers controllers) {
		super(parent, "Focus Work", controllers);
		final GridBagConstraints gbc = SwingUtils.getGBC();
		
		type.addActionListener(new UpdateListener());
		gbc.gridx	= 0;
		gbc.gridy	= 0;
		add(type, gbc);
		
		gbc.gridy	= 1;
		add(input, gbc);
		
		gbc.gridy	= 2;
		add(new ActionButton("Execute", new Dimension(150, 32), this));
		
		try {
			update(getSelection());
		} catch (final Exception e) {
			//	ignored
		}
		
		display();
	}
	
	
	
	protected Parameters getParameters(final String type)
			throws InvalidCurrentUserException {
		if ((type == null) || type.isEmpty()) {
			throw new IllegalArgumentException("Invalid focus type:  " + type);
		} else if (type.startsWith("Deadline")) {
			return new PDeadline();
		} else if (type.startsWith("Duration")) {
			return new PDuration();
		} else if (type.startsWith("Task-type")) {
			return new PTaskType();
		}
		
		throw new IllegalArgumentException("Unknown focus type:  " + type);
	}
	
	@Override
	protected String getSelection() {
		return type.getSelection();
	}
	
	@Override
	protected void handle(ActionEvent event) throws Exception {
		final TaskList tl =
			new TaskList(getOwner(), getControllers(), input.get().read());
		dispose();
		tl.display();
	}
	
	@Override
	protected void update(String selection) throws InvalidCurrentUserException {
		input.set(getParameters(selection));
	}
	
	
	
	
	
	
	
	
	
	
	private abstract class Parameters extends Component {
		private static final long serialVersionUID = 1L;
		
		
		
		/**
		 * Returns the {@link Task}s
		 * 	selected by this {@link Parameters}
		 * 
		 * @throws InvalidCurrentUserException
		 * 				{@link UserController#checkIfUserIsLoggedIn()}
		 * @throws TaskManagerException
		 */
		public abstract Collection<Task> read()
			throws InvalidCurrentUserException, TaskManagerException;
		
	}
	
	
	
	
	
	private class PDeadline extends Parameters {
		private static final long serialVersionUID = 1L;
		
		private final TextInput max = new TextInput();
		
		
		public PDeadline() {
			final GridBagConstraints gbc = getGBC();
			
			gbc.gridx	= 0;
			gbc.gridy	= 0;
			add(new JLabel("Max:  ", SwingConstants.LEFT), gbc);
			
			gbc.gridx	= 1;
			add(max, gbc);
		}
		
		
		
		private final int getMax() {
			try {
				return Integer.parseInt(max.read());
			} catch (final NumberFormatException e) {
				return -1;
			}
		}
		
		@Override
		public Collection<Task> read() throws InvalidCurrentUserException {
			return getControllers().getControllerTasks().getFocusTasksDeadline(
					getMax());
		}
		
	}
	
	
	
	
	
	private class PDuration extends Parameters {
		private static final long serialVersionUID = 1L;
		
		private final TextInput max = new TextInput();
		private final TextInput min = new TextInput();
		
		
		public PDuration() {
			final GridBagConstraints gbc = getGBC();
			
			gbc.gridx	= 0;
			gbc.gridy	= 0;
			add(new JLabel("Min (minutes):  ", SwingConstants.LEFT), gbc);
			
			gbc.gridx	= 1;
			add(min, gbc);
			
			gbc.gridx	= 0;
			gbc.gridy	= 1;
			add(new JLabel("Max (minutes):  ", SwingConstants.LEFT), gbc);
			
			gbc.gridx	= 1;
			add(max, gbc);
		}
		
		
		
		private final int getMax() {
			try {
				return (Integer.parseInt(max.read()) * 60000);
			} catch (final NumberFormatException e) {
				return -1;
			}
		}
		
		private final int getMin() {
			try {
				return (Integer.parseInt(min.read()) * 60000);
			} catch (final NumberFormatException e) {
				return -1;
			}
		}
		
		@Override
		public Collection<Task> read() throws InvalidCurrentUserException {
			return getControllers().getControllerTasks().getFocusTasksDuration(
					getMin(), getMax());
		}
		
	}
	
	
	
	
	
	private class PTaskType extends Parameters {
		private static final long serialVersionUID = 1L;
		
		private ComboSelector<TaskType> type;
		
		
		public PTaskType() throws InvalidCurrentUserException {
			final GridBagConstraints gbc = getGBC();
			
			type = new ComboSelector<TaskType>(
					getControllers().getControllerTasks().getTaskTypes().toArray(
							new TaskType[0]));
			
			gbc.gridx	= 0;
			gbc.gridy	= 0;
			add(type, gbc);
		}
		
		
		
		@Override
		public Collection<Task> read()
				throws InvalidCurrentUserException, TaskManagerException {
			return getControllers().getControllerTasks().getFocusTasksTaskTypes(
					type.getSelection());
		}
		
	}
	
	
	
	
	
	private static class TaskList extends ActionDialog {
		private static final long serialVersionUID = 1L;
		
		private Wrapper<InfoLabel>	info	= new Wrapper<InfoLabel>();
		private SelectionList<Task> tasks;
		
		
		public TaskList(final Frame parent, final Controllers controllers,
				final Collection<Task> tasks) {
			super(parent, "Task list", controllers);
			final GridBagConstraints gbc = SwingUtils.getGBC();
			
			this.tasks = new SelectionList<Task>(tasks, false);
			this.tasks.addListSelectionListener(new ListSelectionListener() {
				@Override
				public void valueChanged(final ListSelectionEvent event) {
					setTask(TaskList.this.tasks.getSelection().get(0));
				}
			});
			
			gbc.gridx	= 0;
			gbc.gridy	= 0;
			gbc.gridheight	= 3;
			add(this.tasks.wrap(new Dimension(300, 500)), gbc);
			
			info.setPreferredSize(new Dimension(500, 500));
			gbc.gridx	= 1;
			add(info, gbc);
			
			gbc.gridx	= 2;
			gbc.gridheight	= 1;
			add(new ActionButton("Change focus",
					new Dimension(150, 32), this, "F"), gbc);
			
			gbc.gridy	= 1;
			add(new ActionButton("Modify details",
					new Dimension(150, 32), this, "M"), gbc);
			
			gbc.gridy	= 1;
			add(new ActionButton("Update status",
					new Dimension(100, 32), this, "S"), gbc);
		}
		
		
		
		protected Task getSelection() {
			return tasks.getSelection().get(0);
		}
		
		@Override
		protected void handle(final ActionEvent event) throws Exception {
			final Task task;
			switch (event.getActionCommand().charAt(0)) {
				case 'F':	//	Focus Work
					dispose();
					new FocusWorkDialog(getOwner(), getControllers());
					break;
					
				case 'M':	//	Modify Task Details
					if ((task = getSelection()) != null) {
						dispose();
						new ModifyTaskDialog(getOwner(), getControllers(), task);
					}
					break;
					
				case 'S':	//	Update Task Status
					if ((task = getSelection()) != null) {
						dispose();
						new UpdateTaskStatusDialog(getOwner(), getControllers(), task);
					}
					break;
					
				default:	//	should never be reached
					throw new InternalError("Invalid parameter");
			}
		}
		
		private final void setTask(final Task task) {
			info.set((task == null) ? null : new InfoLabel(Utils.format(task, false)));
				
			invalidate();
			pack();
		}
		
	}
	
}